//-------------------------------------------------------------------------------------------------
// triangle.hpp - Definition of Triangle shape for physics operations
// (c) 2009 Simon Miessler
//-------------------------------------------------------------------------------------------------

#pragma once

#include <boost/optional.hpp>

#include "math_exports.hpp"
#include "geometry_fwd.hpp"
#include "vector3.hpp"
#include "ray.hpp"
#include "raycastable.hpp"




namespace math
{
	/**
	* Triangle in 3d space.
	*/
	template <typename T>
	class triangle
		: public intersectable< triangle<T> >
		, public raycastable< triangle<T>, T >
	{
	public:

		/// The used floating-point type
		typedef T              float_t;

	public:

		vector3<T>   a;
		vector3<T>   b;
		vector3<T>   c;

		vector3<T>   normal;

	public:

		/**
		 * Create a Triangle.
		 * A is located at (1, 0, 0),
		 * B is located at (-1, 0, 0) and
		 * C is located at (0, 1, 0).
		 *
		 * The normal points to (0, 1, 0)
		 */
		triangle()
			: a(1, 0, 0)
			, b(-1, 0, 0)
			, c(0, 0, 1)
			, normal(0, 1, 0)
		{}

		/**
		 * Create a triangle at the specified positions.
		 *
		 * @param a   the first point for this triangle
		 * @param b   the second point for this triangle
		 * @param c   the third point for this triangle
		 */
		triangle(const vector3<T>& a, const vector3<T>& b, const vector3<T>& c)
			: a(a)
			, b(b)
			, c(c)
			, normal(norm(cross(c - a, b - a)))
		{}

		/**
		 * Create a triangle at the specified positions.
		 * This function can be used when the normal has been already
		 * calculated, to avoid calculating it again.
		 *
		 * @param a        the first point for this triangle
		 * @param b        the second point for this triangle
		 * @param c        the third point for this triangle
		 * @param normal   the normal of the triangle
		 */
		triangle(const vector3<T>& a, const vector3<T>& b, const vector3<T>& c, const vector3<T>& normal)
			: a(a)
			, b(b)
			, c(c)
			, normal(normal)
		{
			#ifdef _DEBUG
				vector3<T> n = norm(cross(c - a, b - a));
				assert(n.x == normal.x);
				assert(n.y == normal.y);
				assert(n.z == normal.z);
			#endif
		}




		/**
		 * Get a bounding sphere that defines the closest
		 * sphere for this triangle.
		 */
		sphere<T> boundingSphere() const
		{
			// We simply calculate the
			// center position of the triangle and find out the largest
			// radius that's needed.
			// This algorithm isn't the best, but it's robust and simple.
			vector3<T> center = a + b + c;
			center /= 3;

			T radius;
			radius = vector3<T>(center - a).length();
			radius = std::max(radius, vector3<T>(center - b).length());
			radius = std::max(radius, vector3<T>(center - c).length());

			return sphere<T>(center, radius);
		}




		/**
		 * Get a Ray starting at A and ending at B.
		 */
		ray<T> ab() const
		{
			return ray<T>(a, b);
		}

		/**
		 * Get a Ray starting at A and ending at C.
		 */
		ray<T> ac() const
		{
			return ray<T>(a, c);
		}

		/**
		 * Get a Ray starting at B and ending at C.
		 */
		ray<T> bc() const
		{
			return ray<T>(b, c);
		}

		/**
		 * Get a Ray starting at B and ending at A.
		 */
		ray<T> ba() const
		{
			return ray<T>(b, a);
		}

		/**
		 * Get a Ray starting at C and ending at A.
		 */
		ray<T> ca() const
		{
			return ray<T>(c, a);
		}

		/**
		 * Get a Ray starting at C and ending at B.
		 */
		ray<T> cb() const
		{
			return ray<T>(c, b);
		}
	};
	///////////////////////////////////////////////////////////////////////////////////////////////




	/**
	 * In order to compile a binary that actually contains the code for our Triangle,
	 * we have to explicitly instantiate the template Triangles and export those:
	 */
	template struct MATH_API intersectable< triangle<float> >;
	template struct MATH_API intersectable< triangle<double> >;
	template struct MATH_API raycastable< triangle<float>, float>;
	template struct MATH_API raycastable< triangle<double>, double>;
	template class MATH_API triangle<float>;
	template class MATH_API triangle<double>;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

